home *** CD-ROM | disk | FTP | other *** search
- // CNTRL0.CPP - "driver" function for RARS - M. Timin, Feb. 1995
- // adapted to ver. 0.39 3/6/95 by M. Timin
-
- /* Easy-To-Understand Robot Driver */
- /*
- This robot driver attempts to stay in the middle of the track at all
- times. She calculates a cornering speed for each corner based on its
- radius. She accelerates on each straightaway until a certain fraction
- of its length is reached; Then she slows down, attempting to arrive at
- the corner with the proper cornering speed. In the corner she attempts
- to maintain the cornering speed while attempting to stay in the middle
- of the track. Her strategy for passing is to choose right or left at
- random, and then throw the car into a sharp slide toward that direction.
- (pretty ballsy, huh!)
- */
- // The language here can be considered to be ANSI C or C++.
-
- #include <string.h>
- #include <stdlib.h>
- #include <math.h>
- #include "car.h"
-
- // These parameters may be adjusted to get better performance:
- const double CORN_SPD_CON = 6.2; // determines how fast to take corners
- const double STEER_GAIN = 2.5; // servo gain, for staying in "lane"
- const double STEER_DAMP = 1.5; // servo damping, to prevent "weaving"
- const double END_ACCEL = .25; // we accelerate until this fraction of length
- const double END_CORNER = 2.5;// used to decide when to start leaving the corer
- const double ESC_ALPHA = .03; // after a crash, set alpha like this, + or -
- const double SLIP_LIM = 3.0; // maximum wheel slip, ft/sec, in wheel_slip()
- const double SLIP_CON = 70.0; //
- const double SHARP_TURN = .1; // change in alpha when attempting to pass
- const int PASSING_TIME = 50; // time to stay in passing maneuver, counts
-
- extern char* glob_name; // The name string, below, will be copied here
-
- // The following function calculates the speed for a corner.
- // The lateral force produced by cornering is proportional to the square
- // of the speed, and is inversely proportional to the radius of the path.
- // Therefore, the attainable cornering speed for differnt radii is
- // proportional to the square root of the radius. This function implements
- // that rule. The value to use for CORN_SPD_CON can be determined by
- // trial and error. Example result: if the car is following a path with
- // radius of 100 ft, and if CORN_SPD_CON is 5.0, then corn_speed is 50 ft/sec.
- double corn_speed(double radius)
- {
- if(radius < 0.0) // change sign of negative radius
- radius = -radius;
- else if(radius == 0.0) // This is just insurance, this funtion doesn't
- return(200.0); // make sense when the radius is zero.
- return CORN_SPD_CON * sqrt(radius);
- }
-
- // In order to set vc, if you know how fast you want to go (goal), and how
- // fast you are going now (present), This function will compute a reasonable
- // value for vc. The value is never very far from the present speed, both
- // to attempt to stay within the power limit, and to maintain steering control.
- // You can adjust the resulting slip by changing SLIP_LIM.
- double wheel_speed(double goal, double present)
- {
- double ws;
-
- if(present > goal + 2 * SLIP_LIM) // if too fast,
- ws = present - SLIP_LIM; // slow down.
- else if(present < goal - 2 * SLIP_LIM) // if too slow,
- ws = present + SLIP_LIM; // accelerate.
- else // if quite close,
- ws = (goal + present) / 2; // approach desired speed gently.
-
- return ws;
- }
-
- /* These two structures from CAR.H are repeated here as comments, because
- the "driver" function receives situation as input and produces con_vec
- as output.
- struct situation { // a car's local situation as seen by the driver
- double cur_rad; // radius of inner wall of curve (0 means straight)
- double cur_len; // length of current track segment (angle if curve)
- double to_lft; // distance to left wall
- double to_rgt; // distance to right wall
- double to_end; // how far to end of current track seg. (angle or feet)
- double v; // the speed of the car, feet per second
- double vn; // component of v perpendicular to track direction
- double nex_len; // length of the next track segment (angle if curve)
- double nex_rad; // radius of inner wall of next segment (or 0)
- double after_rad; // radius of the segment after that one. (or 0)
- double power_req; // ratio: power requested by driver to maximum power
- int dead_ahead; // set when there is a car dead ahead, else 0
- };
- struct con_vec { double alpha, vc; }; // control vector, steering & throttle
- */
-
- // The task of this function is to compute vc and alpha. A high speed
- // car on a track is a little like the keel of a boat; if you set the keel
- // at a slight angle to the direction of the oncoming water, you get a large
- // force to the side. That is how we corner the car. The driver sets the
- // car at a slight angle with respect to its direction of motion, this
- // cause a force to the side, causing the path of the car to curve. The
- // magnitude of the force is proportional to the angle (alpha) for very
- // small alpha, and when there is not much wheel spin. The wheel spin
- // is controlled by vc, which is the rearward speed of the bottom of the
- // tire. When going down the straight at a constant, moderate velocity,
- // then vc is equal to the speed of the car. For acceleration, vc is
- // made a little greater than the speed. For braking, it is made a little
- // less. When accelerating, vc is limited by the power available.
- con_vec cntrl0(situation s)
- {
- const char name[] = "Annie"; // This is the robot driver's name!
- static int init_flag = 1; // cleared by first call
- double speed; // target speed for cornering, ft/sec
- double speed_next; // target speed for next corner
- con_vec result; // This is what is returned.
- double width; // track width, feet
- double alpha, vc; // components of result
- static double alpha_inc = 0.0; // alpha increment during passing maneuver
- static int counting = 0; // will be set and counting down when passing
- // This paragraph has nothing to do with car control; it is just
- // to identify the driver by copying its name to a global RAM area:
- // This happens only on the very first call to this function
-
- if(init_flag) { // first time through, only copy name:
- strcpy(glob_name, name);
- init_flag = 0;
- result.alpha = result.vc = 0;
- return result;
- }
-
- if(stuck(s.backward, s.v,s.vn, s.to_lft,s.to_rgt, &result.alpha,&result.vc))
- return result;
-
- // Set alpha based on a servo-mechanism approach, trying to stay
- // in the middle of the track, i.e., s.to_left equal to .5 * width:
- width = s.to_lft + s.to_rgt; // find width of track
- alpha = STEER_GAIN * (s.to_lft - .5 * width) / width;
- alpha -= STEER_DAMP * s.vn / s.v; // This is damping, to prevent oscillation
-
- // calculate target speeds for current corner and the next:
- speed = corn_speed(s.cur_rad + .5 * width); // speed is based on radius
- speed_next = corn_speed(s.nex_rad + .5 * width); // of center line of track.
-
- // now set the tire speed, vc:
- if(s.cur_rad == 0.0) // If we are on a straightaway,
- if(s.to_end > END_ACCEL * s.cur_len) // if we are far from the end,
- vc = s.v + SLIP_CON / s.v; // keep accellerating near full power
- else // otherwise,
- vc = wheel_speed(speed_next, s.v); // brake for next corner
- else // If we're in the curve, maintain speed.
- if(s.to_end * (s.cur_rad + .5 * width) > END_CORNER * width)
- // if we are far from the next corner, stay at "speed".
- vc = wheel_speed(speed, s.v);
- else // but when we near the next corner, adjust to "speed_next"
- vc = wheel_speed(speed_next, s.v);
-
- // The passing maneuver:
- if(s.dead_ahead & !counting) { // When first encountering the car ahead:
- counting = PASSING_TIME; // setup the timer,
- if(random(29999) < 15000) // choose a right or left maneuver:
- alpha_inc = SHARP_TURN;
- else
- alpha_inc = -SHARP_TURN;
- }
- if(counting) { // If we are still in the passing maneuver,
- alpha += alpha_inc; // change alpha
- --counting; // count down to zero
- }
-
- result.vc = vc; result.alpha = alpha;
- return result;
- }
-